<?php

namespace Trois\Crontab;

use App\HttpController\Open\Mini;
use App\Model\AdMiniProgramModel;
use App\Model\Service\AdCacheService;
use EasySwoole\Component\Singleton;
use EasySwoole\Component\WaitGroup;
use EasySwoole\Crontab\JobInterface;
use EasySwoole\EasySwoole\Core;
use EasySwoole\EasySwoole\Logger;
use Trois\Lib\Help;

/**
 * 检测小程序状态
 */
class CheckMiniStatueCrontab implements JobInterface
{
    use Singleton;

    private string $url = 'http://yiye.jiaoyueyan.cn/mapp.php?app_id=';

    private string $checkCurrentCache = 'system:mini-current-check-pro';

    private int $nums = 1000;

    public function jobName(): string
    {
        return "CheckMiniStatueCrontab";
    }

    public function crontabRule(): string
    {
        return '*/1 * * * *';
    }

    public function run(): void
    {
        $this->handle();
    }

    /**
     * 获取系统时间
     */
    function get_microtime(): float
    {
        list($usec, $sec) = explode(' ', microtime()); //取系统时间 前半部分是时间戳，后半部分是微妙部分
        return ((float)$usec + (float)$sec);
    }

    public function handleBak(): bool
    {

//        Logger::getInstance()->info("开始处理 CheckMiniStatueCrontab");

        $checkCurrent = Help::redisPoolStringInvoke($this->checkCurrentCache);

//        dd($checkCurrent);

        $checkCurrent = $checkCurrent ?: 0;

        $res = \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($checkCurrent) {

            return $redis->ZRANGE(Mini::$key, $checkCurrent, ($checkCurrent + $this->nums));

        });

        $res = $res ?? [];

//        dd($res);

        $checkCurrent = $checkCurrent + $this->nums + 1;

        if (!$res || count($res) < ($this->nums + 1)) {
            $checkCurrent = 0;
        }

//        dd($checkCurrent);

        Help::redisPoolStringInvoke($this->checkCurrentCache, $checkCurrent, 86400 * 365);

        $handles = [];

        $StartTime = $this->get_microtime();

        $wait = new WaitGroup();

        foreach ($res as $re) {

            \co::sleep(0.05);

            $wait->add();

            go(function () use ($wait, $re, &$handles) {

                $_re = explode('-', $re);

                $app_id = $_re[0];

                // 爱点击的直接跳过
                if ($app_id != 'wx00fea72f01312a8d') {
                    $handles[$re] = Help::curl('get', ($this->url . $app_id), [], [], 1);
                }

                $wait->done();

            });

        }

//        dd($handles);
        $wait->wait();

        $StopTime = $this->get_microtime();

//        dd(round(($StopTime - $StartTime), 4));

        Logger::getInstance()->info("CheckMiniStatueCrontab 返回成功：" . count($handles));

        $fail = [];

        $remove = [];
        $success = [];

        foreach ($handles as $re => $response) {


            if ($response['response_code'] === 0) {

                $_re = explode('-', $re);

                // 爱点击的直接跳过
                if ($_re[1] == 242) {
                    continue;
                }

                $_brandId = $_re[2];

                if (!$_brandId) {
                    $remove[] = $re;
                    continue;
                }

                if (!$response['response_content']['state']) {

                    if ($response['response_content']['msg'] != '小程序正常') {

                        $fail[] = [
                            'id' => $_re[1],
                            'status' => $response['response_content']['error_code'],
                            'status_fail_message' => $response['response_content']['msg'] . ',原因：' . $response['response_content']['reason'],
                            'update_time' => time(),
                        ];

                    }

                    $remove[] = $re;

                } else {

                    $success[] = [
                        'id' => $_re[1],
                        'update_time' => time(),
                    ];

                }
            }

        }

//        dd($fail);
//        dd($success);
//        dd($remove);

//        Logger::getInstance()->info("CheckMiniStatueCrontab 成功小程序：." . count($success));
//        Logger::getInstance()->info("CheckMiniStatueCrontab 失效小程序：." . count($fail));
//        Logger::getInstance()->info("CheckMiniStatueCrontab 删除队列：." . count($remove));

        $fail && Help::modelInvoke(AdMiniProgramModel::class, function ($model) use ($fail) {
            $model->batchUpdate($fail);
            $this->refreshCache($fail);
        });

        $success && Help::modelInvoke(AdMiniProgramModel::class, function ($model) use ($success) {
            $model->batchUpdate($success);
        });

        \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($remove) {
            $remove && $redis->zRem(Mini::$key, ...$remove);
        });

        return true;
    }

    public function handle(): bool
    {

//        Logger::getInstance()->info("开始处理 CheckMiniStatueCrontab");

        $checkCurrent = Help::redisPoolStringInvoke($this->checkCurrentCache);

//        dd($checkCurrent);

        $checkCurrent = $checkCurrent ?: 0;

        $res = \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($checkCurrent) {

            return $redis->ZRANGE(Mini::$key, $checkCurrent, ($checkCurrent + $this->nums));

        });

        $res = $res ?? [];

//        dd($res);

        $checkCurrent = $checkCurrent + $this->nums + 1;

        if (!$res || count($res) < ($this->nums + 1)) {
            $checkCurrent = 0;
        }

//        dd($checkCurrent);

        Help::redisPoolStringInvoke($this->checkCurrentCache, $checkCurrent, 86400 * 365);

        $handles = [];

        $StartTime = $this->get_microtime();

        $wait = new WaitGroup();

        libxml_use_internal_errors(true);

        foreach ($res as $re) {


            \co::sleep(0.05);

            $wait->add();

            $_re = explode('-', $re);

            $app_id = $_re[0];

            // 爱点击的直接跳过
            if ($app_id == 'wx00fea72f01312a8d') {
                continue;
            }

            go(function () use ($wait, $re, &$handles) {

                $_re = explode('-', $re);

                $app_id = $_re[0];

                $ql = (new \QL\QueryList)->get('https://mp.weixin.qq.com/mp/waerrpage', [
                    'appid' => $app_id,
                    'type' => 'offshelf',
                    'offshelf_type' => '0#wechat_redirect',
                ], [
                    'headers' => [
                        'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
                    ]
                ]);

                $msgTitle = trim($ql->find('.weui-msg__title')->html());
                $msgDesc = '';

                // 正常的小程序 .weui-msg__title 没有值
                // 处理异常的小程序即可
                if (trim($msgTitle) != '') {

                    // 单独处理
                    if (strpos($msgTitle, 'write_ban_title') !== false) {

                        $data2 = explode(',', $msgTitle);
                        if (strpos($data2[1], '3') !== false) {
                            $msgDesc = 'Mini Program is out of service';
                        }

                    } else if (
                        strpos($msgTitle, '已暂停') !== false ||
                        strpos($msgTitle, '已注销') !== false ||
                        strpos($msgTitle, '已冻结') !== false ||
                        strpos($msgTitle, '已') !== false
                    ) {

                        $msgDescDom = $ql->find('.weui-msg__desc');
                        $msgDescDom->find('script,a')->remove();
                        $msgDesc = trim($msgDescDom->html());
                        $msgDesc = $msgDesc;

                    }

                }

                $ql->destruct();

                $handles[$re] = empty($msgDesc) ? [
                    'type' => 'success',
                    'status' => 1,
                    'msgTitle' => $msgTitle,
                    'msgDesc' => $msgDesc,
                ] : [
                    'type' => 'error',
                    'status' => 999,
                    'msgTitle' => $msgTitle,
                    'msgDesc' => $msgDesc,
                ];

                $wait->done();

            });

        }

//        dd($handles);
        $wait->wait();

        $StopTime = $this->get_microtime();

//        dd(round(($StopTime - $StartTime), 4));

        Logger::getInstance()->info("CheckMiniStatueCrontab 返回成功：" . count($handles));

        $fail = [];

        $remove = [];
        $success = [];

//        dd($handles);

        foreach ($handles as $re => $response) {

            $_re = explode('-', $re);

            // 爱点击的直接跳过
            if ($_re[1] == 242) {
                continue;
            }


            $_brandId = $_re[2];

            if (!$_brandId) {
                $remove[] = $re;
                continue;
            }

            if ($response['type'] == 'error') {

                $fail[] = [
                    'id' => $_re[1],
                    'status' => $response['status'],
                    'status_fail_message' => $response['msgTitle'] . ',原因：' . $response['msgDesc'],
                    'update_time' => time(),
                ];

                $remove[] = $re;

            } else {

                $success[] = [
                    'id' => $_re[1],
                    'update_time' => time(),
                ];

            }


        }

//        Logger::getInstance()->info("CheckMiniStatueCrontab 失效小程序：." . count($fail) / 2);

        $fail && Help::modelInvoke(AdMiniProgramModel::class, function ($model) use ($fail) {
            $model->batchUpdate($fail);
            $this->refreshCache($fail);
        });

//        dd($fail);
//        dd($success);
//        dd($remove);

        $success && Help::modelInvoke(AdMiniProgramModel::class, function ($model) use ($success) {
            $model->batchUpdate($success);
        });

        \EasySwoole\RedisPool\RedisPool::invoke(function (\EasySwoole\Redis\Redis $redis) use ($remove) {
            $remove && $redis->zRem(Mini::$key, ...$remove);
        });

        return true;
    }

    public function refreshCache(array $fail = []): void
    {

        if (!$fail) {
            return;
        }

        $ids = [];
        foreach ($fail as $f) {
            $ids[] = $f['id'];
        }

        $AdCacheService = new AdCacheService();
        $AdCacheService->refresh([
            'tableName' => 'by_crm_ad_mini_program',
            'id' => implode(',', $ids),
            'data' => [],
        ]);
    }

    public function onException(\Throwable $throwable)
    {
        Logger::getInstance()->error("CheckMiniStatueCrontab onException:" + $throwable->getMessage());
    }

}